package org.zoomdev.zoom.aop.modules;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.aop.AopFactory;
import org.zoomdev.zoom.aop.FilteredInterceptorFactory;
import org.zoomdev.zoom.aop.Interceptor;
import org.zoomdev.zoom.aop.InterceptorFactory;
import org.zoomdev.zoom.aop.Invoker;
import org.zoomdev.zoom.aop.MethodInvokerFactory;
import org.zoomdev.zoom.aop.factory.AnnotationInterceptorFactory;
import org.zoomdev.zoom.aop.factory.AopInterceptorFactory;
import org.zoomdev.zoom.aop.factory.ClassAndMethodFilterInterceptorFactory;
import org.zoomdev.zoom.aop.impl.InterceptorChan;
import org.zoomdev.zoom.aop.utils.AopUtils;
import org.zoomdev.zoom.common.Destroyable;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.filter.ClassFilter;
import org.zoomdev.zoom.common.filter.MethodFilter;
import org.zoomdev.zoom.common.filter.impl.PatternClassAndMethodFilter;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.common.utils.OrderedList;
import org.zoomdev.zoom.ioc.IocConstructor;
import org.zoomdev.zoom.ioc.IocEnhancer;
import org.zoomdev.zoom.ioc.impl.factories.ClassIocFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

abstract class AbstractMethodInterceptorFactory implements AopFactory, IocEnhancer, Destroyable {


    /**
     * 所有增强类名称都以这个未结尾
     */
    public static final String TAIL = AopUtils.TAIL;
    public static final String INVOKER_FIELD_NAME = "_$invokers";
    protected static final Logger log = LoggerFactory.getLogger(CoreAopModule.NAME);
    private final List<Invoker> invokers = Collections.synchronizedList(new ArrayList<>());
    protected ClassLoader classLoader;


    /**
     * 这里暂时只允许静态
     */
    private Map<Class<?>, Class<?>> enhanceMap;

    /**
     * list
     */
    private OrderedList<FilteredInterceptorFactory> factories;

    private final AnnotationGroup annotationGroup;

    AbstractMethodInterceptorFactory(ClassLoader classLoader) {

        this.factories = new OrderedList<>();

        enhanceMap = new ConcurrentHashMap<>();
        this.classLoader = classLoader;
        annotationGroup = new AnnotationGroup();
        addFactory(annotationGroup,0);
    }


    /**
     * 销毁
     */
    public void destroy() {
        if (enhanceMap != null) {
            Classes.destroy(enhanceMap);
            enhanceMap = null;
        }

        if (factories != null) {
            Classes.destroy(factories);
            factories = null;
        }


    }

    @Override
    public AopFactory addFactory(InterceptorFactory factory, String pattern, int order) {
        addFactory(new ClassAndMethodFilterInterceptorFactory( new PatternClassAndMethodFilter(pattern) , factory),order);
        return this;
    }

    @Override
    public AopFactory addFactory(InterceptorFactory factory, MethodFilter filter, int order) {
        addFactory(new ClassAndMethodFilterInterceptorFactory(filter, factory), order);
        return this;
    }


    private static class AnnotationGroup implements FilteredInterceptorFactory{

        private final List<AnnotationInterceptorFactory<?>> list = new ArrayList<>();
        private final Map<Class<?>,AnnotationInterceptorFactory<?>> map = new HashMap<>();

        public void add(AnnotationInterceptorFactory<?> factory){
            list.add(factory);
            map.put(factory.getType(),factory);
        }

        @Override
        public boolean accept(Class<?> targetClass, Method method) {
            for(AnnotationInterceptorFactory<?> factory : list){
                if(factory.accept(targetClass,method)){
                    return true;
                }
            }
            return false;
        }

        @Override
        public void createInterceptors(Class<?> targetClass, Method method, List<Interceptor> interceptors) {
            Annotation[] annotations = method.getAnnotations();
            for(Annotation annotation : annotations){
                Class<?> name = annotation.annotationType();
                AnnotationInterceptorFactory factory = map.get(name);
                if(factory!=null){
                    factory.createInterceptors(annotation,targetClass,method,interceptors);
                }
            }

        }
    }

    @Override
    public AopFactory addFactory(FilteredInterceptorFactory factory, int order) {
        if(factory instanceof AnnotationInterceptorFactory){
            annotationGroup.add((AnnotationInterceptorFactory)factory);
            return this;
        }
        factories.add(factory, order);
        return this;
    }

    public Class<?> enhance(Class<?> clazz) {
        assert (clazz != null);

        if (clazz.getName().endsWith(TAIL)) {
            return clazz;
        }

        if (log.isDebugEnabled()) {
            log.debug("Try to enhance:" + clazz);
        }

        Class<?> enhanced = enhanceMap.get(clazz);
        if(enhanced==null){
            enhanced = create(clazz);
            enhanceMap.putIfAbsent(clazz,enhanced);
        }

        return enhanced;
    }


    private Class<?> create(Class<?> targetClass){
        Class<?> enhanced;
        try {
            //热重启 的时候会，目前没有这个需求
            enhanced = Class.forName(targetClass.getName() + TAIL,
                    false, classLoader);
            if (log.isDebugEnabled()) {
                log.debug("类已经被增强，直接返回" + enhanced);
            }

        } catch (ClassNotFoundException ex) {
            try {
                List<AopConfig> _configs = createConfigs(targetClass);
                if (_configs == null) {
                    return targetClass;
                }
                enhanced = enhance(targetClass, _configs.toArray(new AopConfig[0]));
                if(enhanced!=targetClass){
                    if (log.isDebugEnabled()) {
                        log.debug("Enhance class:" + targetClass.getName() + " ===> " + enhanced.getName());
                    }
                }
                //重新初始化一下
                try {
                    setConfigs(enhanced, targetClass, _configs.toArray(new AopConfig[0]));
                } catch (Exception e) {
                    throw new ZoomException("设置原有增强类失败," + targetClass, Classes.getCause(e));
                }
            } catch (Exception e) {
                throw new ZoomException("增强类失败," + targetClass, Classes.getCause(e));
            }
        }

        return enhanced;
    }

    private final List<ClassFilter> blackList = new ArrayList<>();
    public void addBlackList(ClassFilter filter){
        blackList.add(filter);
    }
    public boolean canEnhance(Class<?> targetClass) {
        for(ClassFilter filter : blackList){
            if(!filter.accept(targetClass)){
                return false;
            }
        }

        List<Method> methods = Classes.findMethods(targetClass, Classes.NOT_PRIVATE_FINAL);
        OrderedList<FilteredInterceptorFactory> factories = this.factories;
        for (Method method : methods) {
            for (int i = 0, c = factories.size(); i < c; ++i) {
                FilteredInterceptorFactory factory = factories.get(i);
                if (factory.accept(targetClass, method)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public Invoker getInvoker(Class<?> instanceClass, Method method, Class<?> interceptorClass) {
        for (Invoker invoker : invokers) {
            InterceptorChan chan = (InterceptorChan) invoker;
            if (chan.getMethod().equals(method)) {
                Invoker ret = chan.getInvoker(interceptorClass);
                if (ret != null) {
                    return ret;
                }
            }
        }
        return null;
    }

    @Override
    public IocConstructor enhance(IocConstructor constructor) {
        if (constructor instanceof ClassIocFactory.ClassIocConstructor) {
            ClassIocFactory.ClassIocConstructor c = (ClassIocFactory.ClassIocConstructor) constructor;
            Class clazz = c.getConstructor().getDeclaringClass();
            Class enhanced = enhance(clazz);
            if (enhanced != clazz) {
                c.setType(enhanced);
            }
        }


        //Class enhanced = enhance((Class<?>) key.getType());

        return constructor;
    }

    @Override
    public ClassLoader getClassLoader() {
        return classLoader;
    }

    private List<AopConfig> createConfigs(Class<?> targetClass) {
        List<Method> methods = Classes.findMethods(targetClass, Classes.NOT_PRIVATE_FINAL);
        List<AopConfig> configs = null;
        OrderedList<FilteredInterceptorFactory> factories = this.factories;
        List<Interceptor> interceptors = new ArrayList<>();
        for (Method method : methods) {
            interceptors.clear();
            for (int i = 0, c = factories.size(); i < c; ++i) {
                InterceptorFactory factory = factories.get(i);
                factory.createInterceptors(targetClass, method, interceptors);
            }
            if (!interceptors.isEmpty()) {
                if (configs == null) {
                    configs = new ArrayList<>();
                }
                configs.add(new AopConfig(method, interceptors.toArray(new Interceptor[0])));
            }
        }
        return configs;
    }
//    @Override
//    public IocConstructor createConstructor(IocKey key) {
//        if (!ClassIocFactory.accept(key)) {
//            return null;
//        }
//
//        if (!Classes.isClass(key.getType())) {
//            return null;
//        }
//
//        Class enhanced = enhance((Class<?>) key.getType());
//        Constructor constructor = ClassIocFactory.getConstructor(enhanced);
//        if (constructor == null) {
//            return null;
//            //throw new IocException("不能对类" + enhanced + "创建IocConstructor,请提供至少一个可访问的构造函数，否则系统无法判断应该使用哪一个构造函数来创建对象.");
//        }
//        return new ClassIocFactory.ClassIocConstructor(IocKey.fromConstructor(key, constructor), constructor);
//    }

    protected abstract Class<?> enhance(Class<?> src, AopConfig[] aopConfigs) throws Exception;

    protected void setConfigs(Class<?> enhanced, Class<?> orgClass, AopConfig[] configs) throws Exception {
        for (int i = 0, c = configs.length; i < c; ++i) {
            AopConfig aopConfig = configs[i];
            if (aopConfig.interceptors.length == 0) {
                continue;
            }
            final Method newMethod = enhanced.getDeclaredMethod("__" + aopConfig.method.getName(), aopConfig.method.getParameterTypes());
            Invoker invoker = InterceptorChan.make(aopConfig.interceptors, newMethod, aopConfig.method, MethodInvokerFactory.FACTORY);
            Classes.set(enhanced, INVOKER_FIELD_NAME + i, invoker);
            invokers.add(invoker);
        }
    }

    public static class AopConfig {
        public final Interceptor[] interceptors;
        public final Method method;

        public AopConfig(Method method, Interceptor[] interceptors) {
            this.method = method;
            this.interceptors = interceptors;
        }

    }

}
